package com.joizhang.seckill.controller;

import com.joizhang.seckill.api.kafka.SecKillMessage;
import com.joizhang.seckill.api.redis.GoodsKey;
import com.joizhang.seckill.api.service.GoodsService;
import com.joizhang.seckill.api.service.OrderService;
import com.joizhang.seckill.api.service.SecKillService;
import com.joizhang.seckill.api.service.exception.CodeMsg;
import com.joizhang.seckill.api.vo.OrderVo;
import com.joizhang.seckill.api.vo.SecKillGoodsVo;
import com.joizhang.seckill.api.vo.UserVo;
import com.joizhang.seckill.limit.ApiRateLimiter;
import com.joizhang.seckill.limit.LimitType;
import com.joizhang.seckill.utils.Result;
import com.joizhang.seckill.utils.WebCodeMsg;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Controller
@RequestMapping("/seckill")
public class SecKillController implements InitializingBean {

    /**
     * 做标记，判断该商品是否被处理过了
     */
    public static final Map<Long, Boolean> localOverMap = new HashMap<>();

    @DubboReference(mock = "fail:return null")
    private GoodsService goodsService;

    @DubboReference
    private OrderService orderService;

    @DubboReference
    private SecKillService seckillService;


    /**
     * 系统初始化,将商品信息加载到redis和本地内存
     */
    @Override
    public void afterPropertiesSet() {
        List<Long> goodsIdsFromCache = goodsService.getGoodsIdsFromCache();
        if (CollectionUtils.isEmpty(goodsIdsFromCache)) {
            return;
        }
        for (Long goodsId : goodsIdsFromCache) {
            SecKillGoodsVo secKillGoodsVo = goodsService.getGoodsDetailFromCache(goodsId);
            goodsService.setStock(GoodsKey.GOODS_STOCK, secKillGoodsVo.getId().toString(), secKillGoodsVo.getStockCount());
            // 初始化商品都是没有处理过的
            localOverMap.put(goodsId, false);
        }
    }

    @RequestMapping(value = "/init", method = RequestMethod.GET)
    @ResponseBody
    public Result<CodeMsg> secKillInit() {
        afterPropertiesSet();
        return Result.success(WebCodeMsg.SUCCESS);
    }

    /**
     * GET POST
     * 1、GET幂等,服务端获取数据，无论调用多少次结果都一样
     * 2、POST，向服务端提交数据，不是幂等
     * <p>
     * 将同步下单改为异步下单
     */
    @ApiRateLimiter(qps = 1000, limitType = LimitType.METHOD)
    @RequestMapping(value = "/do_seckill", method = RequestMethod.POST)
    @ResponseBody
    public Result<CodeMsg> doSecKill(UserVo userVo, @RequestParam("goodsId") long goodsId) {
        if (Objects.isNull(userVo)) {
            return Result.error(WebCodeMsg.SESSION_ERROR);
        }
        // 内存标记，减少redis访问
        boolean over = localOverMap.get(goodsId);
        if (over) {
            return Result.error(WebCodeMsg.SECKILL_OVER);
        }
        // 预减库存
        long stock = seckillService.decrement(GoodsKey.GOODS_STOCK, "" + goodsId);
        if (stock < 0) {
            afterPropertiesSet();
            long stock2 = seckillService.decrement(GoodsKey.GOODS_STOCK, "" + goodsId);
            if (stock2 < 0) {
                localOverMap.put(goodsId, true);
                return Result.error(WebCodeMsg.SECKILL_OVER);
            }
        }
        // 判断重复秒杀
        OrderVo order = orderService.getOrderFromCache(userVo, goodsId);
        if (order != null) {
            return Result.error(WebCodeMsg.REPEAT_SECKILL);
        }
        // 入队
        SecKillMessage message = new SecKillMessage();
        UserVo toBeSend = new UserVo();
        toBeSend.setId(userVo.getId());
        toBeSend.setUsername(userVo.getUsername());
        message.setUser(toBeSend);
        message.setGoodsId(goodsId);
        Boolean success = seckillService.sendSecKillMessage(message);
        if (!success) {
            return Result.error(WebCodeMsg.SERVER_ERROR);
        }
        return Result.success(WebCodeMsg.SECKILL_SUCCESS);
    }

    /**
     * orderId：成功
     * -1：秒杀失败
     * 0： 排队中
     */
    @RequestMapping(value = "/result", method = RequestMethod.GET)
    @ResponseBody
    public Result<Long> secKillResult(UserVo userVo, @RequestParam("goodsId") long goodsId) {
        if (userVo == null) {
            return Result.error(WebCodeMsg.SESSION_ERROR);
        }
        long orderId = seckillService.getSecKillResult(userVo, goodsId);
        return Result.success(orderId);
    }
}
